home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Programmer Disk
/
The Programmer Disk (Microforum).iso
/
xpro
/
tutor
/
pro1
/
app1.doc
< prev
next >
Wrap
Text File
|
1990-08-02
|
29KB
|
736 lines
i
THE PC ASSEMBLER HELPER
"The PC Assembler Helper" is an object module (ASMHELP.OBJ) which
is designed as a companion for "The PC Assembler Tutor". It can
also be used as an aid in the development of assembler programs
and subprograms. It allows for input to and output from the
assembler level, as well as displaying the data in all the 8086
registers. Part 1 will give a discription of all callable
subroutines and part 2 will explain how to correctly link a
program to ASMHELP.OBJ.
THE SUBROUTINES
There are a number of routines for displaying data and for
inputting data. They all follow a standard format.
(1) All subroutines which display output on the monitor at
the current cursor position start with the word "print_". On
the screen, all hex output is followed by an 'H'. All signed
output has a + or a -. Unsigned output has no sign. All
binary output is distinguishable because it is either 8
digits or 16 digits long. ASCII output is followed by a
single asterisk '*' under normal conditions. If one or more
of the ASCII characters cannot be printed (i.e. if it is
less than 33d or it is 127d or 255d){1} then it will be
displayed as a two digit hex number instead of a single
character. A double asterisk '**' is then used to signal the
presence of a hex number in an ASCII format. The only time
there might be confusion is if one of the characters is also
an asterisk.
(2) All subroutines which get input from the keyboard start
with the word "get_". They all display prompts to tell you
what kind of input is desired.
(3) One byte input or output is passed through register AL.
One word (two byte) input or output is passed through
register AX. Any input or output that is longer than two
bytes is passed by reference. The offset address of the data
is put into AX before calling the subroutine.
Each subroutine returns with all 8086 registers (including the
flags register) unchanged from when the subroutine was called.
As an example, if "variable3" is the name of a variable (in
memory) which we want to print as a signed number, the proper way
to set up for the calls is:
____________________
1 In this chapter, as in all others, 'd' stands for decimal,
'h' for hex.
______________________
The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
The PC Assembler Tutor ii
______________________
ONE BYTE:
mov al, variable3
call print_signed_byte
ONE WORD:
mov ax, variable3
call print_signed
FOUR BYTES:
lea ax, variable3
call print_signed_4byte
EIGHT BYTES:
lea ax, variable3
call print_signed_8byte
For a byte or a word we use the data itself, while for data
larger than a word we pass the data by reference. The same
applies for getting input. Here are the subroutines:
get_num
Enters a number 5 digits or less, either signed or unsigned.
It does no checking to see if the number is too positive or
too negative. Returns a two byte value in AX.
print_num
Prints a word value (from AX) in its signed, unsigned, hex,
ASCII, and binary representation.
All the other input routines do strict error checking. If an
illegal character is entered or if the input is out of range, the
subroutine will ask for new input until it receives valid input.
get_string
Enters a string of 79 characters or less, and puts a 00h at
the end of the string (a C string with a maximum total
length of 80 bytes). The address of the string must be in AX
before making the call.
print_string
Displays a 00h terminated string (a C string) on the
monitor. The address of the first byte of the string must be
in AX before making the call.
get_ascii_byte
Enters a single ascii character and returns it in AL.
print_ascii_byte
Displays one byte (from AL) as an ascii character.
get_ascii
Enters one or two characters and returns it (them) in AX.
Appendix I - The PC Assembler Helper iii
____________________________________
print_ascii
Displays the value in AX as two characters.
get_bcd
Enters a one to eighteen digit signed number as a 10 byte
bcd number. Commas are allowed. The address of the bcd
variable must be in AX before calling the routine.
print_bcd
Displays a 10 byte bcd number as a one to eighteen digit
signed number with commas. The address of the bcd number
must be in AX before calling the subroutine.
get_binary_byte
Enters a one to eight digit binary number and returns it in
AL.
print_binary_byte
Displays a one byte number (from AL) as an eight digit
binary number.
get_binary
Enters a one to sixteen digit binary number and returns it
in AX.
print_binary
Displays a one word number (from AX) as a sixteen digit
binary number.
get_hex_byte
Enters a one or two digit hex number and returns it in AL.
print_hex_byte
Displays a one byte number (in AL) as two hex digits.
get_hex
Enters a one to four digit hex number and returns it in AX.
print_hex
Displays a one word number (from AX) as a four digit hex
number.
get_signed_byte
Enters a one byte signed number (-128 to +127) and returns
it in AL.
print signed_byte
Displays a one byte number (from AL) as a signed number
The PC Assembler Tutor iv
______________________
(-128 to +127).
get_signed
Enters a one word signed number (-32768 to +32767) and
returns it in AX.
print_signed
Displays a one word number (from AX) as a signed number
(-32768 to +32767).
get_signed_4byte
Enters a 4 byte signed number (-2,147,483,648 to
+2,147,483,647). Commas are allowed. The address of the 4
byte number must be in AX before calling the subroutine.
print_signed_4byte
Displays a 4 byte signed number (-2,147,483,648 to
+2,147,483,647) with commas. The address of the 4 byte
number must be in AX before calling the subroutine.
get_signed_8byte
Enters an 8 byte signed number (-9,223,372,036,854,775,808
to +9,223,372,036,854,775,807). Commas are allowed. The
address of the 8 byte number must be in AX before calling
the subroutine. The screen prompt will show the last 3
negative digits as -807 instead of -808, but this is because
of lack of screen space.
print_signed_8byte
Displays an 8 byte signed number (-9,223,372,036,854,775,808
to +9,223,372,036,854,775,807) with commas. The address of
the 8 byte number must be in AX before calling the
subroutine.
get_unsigned_byte
Enters a one byte unsigned number (0 to 255) and returns it
in AL.
print_unsigned_byte
Displays a one byte number (from AL) as an unsigned number
(0 to 255).
get_unsigned
Enters a one word unsigned number (0 to 65535) and returns
it in AX.
print_unsigned
Displays a one word number (from AX) as an unsigned number
(0 to 65535).
get_unsigned_4byte
Enters a 4 byte unsigned number (0 to 4,294,967,295). Commas
are allowed. The address of the 4 byte number must be in AX
before calling the subroutine.
Appendix I - The PC Assembler Helper v
____________________________________
print_unsigned_4byte
Displays a 4 byte unsigned number (0 to 4,294,967,295) with
commas. The address of the 4 byte number must be in AX
before calling the subroutine.
get_unsigned_8byte
Enters an 8 byte unsigned number (0 to
18,446,744,073,709,551,615). Commas are allowed. The address
of the 8 byte number must be in AX before calling the
subroutine.
print_unsigned_8byte
Displays an 8 byte unsigned number (0 to
18,446,744,073,709,551,615) with commas. The address of the
8 byte number must be in AX before calling the subroutine.
Some of the above routines allow commas to be used for data
entry. These routines strip the commas before looking at the
number, so the following numbers all give the equivalent input:
2134875
2,134,875
21,,,34875
2,1,3,4,8,7,5
21,34,87,5
show_regs
Displays the 8086 registers on the top of the screen. Each
call to show_regs increments a resettable counter so you can
know where you are in the program. The counter starts with
an initial value of 0. It also scrolls the screen up if the
cursor is past line 19.
show_regs_and_wait
The same as show_regs except that it waits for you to press
ENTER before continuing.
set_count
Sets the counter in show_regs to the value in AX. The new
counter value (incremented by 1) will appear the next time
show_regs is called.
set_blue
If you have a color monitor which is displaying color text,
it sets the background to blue.
get_continue
Waits for you to press ENTER before continuing the program.
It enters no data. See also set_timer below.
set_timer
In order to allow use with a debugger, it is possible to set
a timer so the print functions keep control of the screen
for a specific amount of time. To use set_timer, you put a
number from 1 to 5 in AL, and call set_timer. This will
cause a 1 to 5 second delay every time a print function or
The PC Assembler Tutor vi
______________________
show_regs is called. A 0 in AL will reset the timer to 0. A
number larger than 5 in AL will cause ASMHELP to wait for
you to press the ENTER key every time a print function or
show_regs is called.
kill_timer
Resets the timer to 0.
set_reg_style
Sets the display style of the individual registers. The
correct order of the definition is:
AX, BX, CX, DX, SI, DI, BP, SP
The style values are:
FULL REGISTER OR RIGHT HALF REGISTER
signed = 1d 1h
unsigned = 2d 2h
binary = 3d 3h
hex = 4d 4h
ascii = 5d 5h
plus (if applicable)
LEFT HALF REGISTER AND HALF REG. BIT
signed = 144d 90h
unsigned = 160d A0h
binary = 176d B0h
hex = 192d C0h
ascii = 208d D0h
set_reg_style makes a COPY of the information, which is used
the next (and subsequent) times that show_regs is called.
The next several paragraphs explain how this works. AX must
contain the address of the 8 byte style definition array
before calling set_reg_style.
REGISTER DISPLAY STYLE
The 8086 contains a number of different registers. Some of these
always contain addresses and are always displayed in hex. These
are CS, DS, ES, SS and IP. They are always in hex and cannot be
changed. Also, each flag has an unchangeable style which will be
explained later.
This still leaves us with AX, BX, CX, DX, SI, DI, BP and SP. Any
of these registers can be displayed in any style desired. In
addition, AX, BX, CX and DX can each be broken into two half
registers, and each half register has an independent style. The
default style is full register, unsigned. If you call show_regs
without setting a style, the eight abovementioned registers will
all display full, unsigned numbers.
Appendix I - The PC Assembler Helper vii
____________________________________
In order to change the style, you need to set up an 8 byte style
definition array in your data segment. The correct definition for
this is the following:
ax_byte db 2
bx_byte db 2
cx_byte db 2
dx_byte db 2
si_byte db 2
di_byte db 2
bp_byte db 2
sp_byte db 2
This is the order that ASMHELP.OBJ expects. It is also the order
that the registers appear on the screen. The number 2 is the
default value for each byte. Once you have defined the 8 bytes,
you may alter any of them that you want to.
The possible styles are signed, unsigned, binary, hex, and ascii.
If you look at a byte:
76543210
HLLL0RRR
the leftmost bit (80h or 128d) signals a half or a full register.
If it is 1, you get a half register, if it is 0, you get a full
register. bits 4,5 and 6 represent the left half register (if
appropriate), while bits 0,1 and 2 represent either the full
register or the right half register. For SI, DI, BP and SP, only
bits 0, 1 and 2 are significant. For AX, BX, CX and DX, all bits
except bit 3 are significant.
The code for bits 0, 1 and 2 is the following:
signed = 1d 1h
unsigned = 2d 2h
binary = 3d 3h
hex = 4d 4h
ascii = 5d 5h
This is the correct code for either a full register or the right
half register.
If you want half registers, you must add 80h (128d) and the code
for the left half register. We have for the left half register:
DECIMAL HEX
signed = 16d + 128d 10h + 80h
unsigned = 32d + 128d 20h + 80h
binary = 48d + 128d 30h + 80h
hex = 64d + 128d 40h + 80h
ascii = 80d + 128d 50h + 80h
which is the same as the above codes but shifted left 4 bits.
Since the 128d and the left half register code always appear in
The PC Assembler Tutor viii
______________________
tandem, we may simply add them together. This gives us:
DECIMAL HEX
signed = 144d 90h
unsigned = 160d A0h
binary = 176d B0h
hex = 192d C0h
ascii = 208d D0h
Here are some examples:
signed left + hex right = 144 + 4 = 148
ascii left + unsigned right = 208 + 2 = 210
binary left + ascii right = 176 + 5 = 181
Simply move the constant to the appropriate byte:
mov cx_byte, 210 ; ascii left, unsigned right
mov ax_byte, 5 ; full register, ascii
mov si_byte, 1 ; full register signed
mov dx_byte, 148 ; signed left, hex right
mov bx_byte, 4 ; full register, hex
After you have changed all the styles you want to, put the
address of ax_byte (the first byte of the array) in ax and call
set_reg_style:
lea ax, ax_byte
call set_reg_style
ASMHELP.OBJ uses the address in AX and stores a COPY of those 8
bytes. The next time you call show_regs, it will use this copy to
define the styles. If you make a mistake, it will default to
unsigned.
Registers are displayed the same way as with the 'print_'
subroutines. Unsigned numbers have no sign. Signed numbers have a
+ or -, hex has an 'H', binary is either 8 or 16 digits, and
ascii has a single asterisk '*' unless one of the characters is
not printable ( 00d to 32d, 127d and 255d) in which case the non-
printable character will be written as a two digit hex number,
and a double asterisk will signal the event.
Finally, the flags are displayed as follows:
OF, IEF, TF, ZF, AF, and CF are blank if they are not set and
have an X if they are set.
DF has a + or a - to indicate increment or decrement.
SF has a + or a - to indicate the sign.
PF has E (for even) or O (for odd).
Appendix I - The PC Assembler Helper ix
____________________________________
LINKING
In order to use ASMHELP.OBJ you must have some standard segments
in your program. The standard code segment is defined as:
CODESTUFF SEGMENT PUBLIC 'CODE'
and the standard data segment is:
DATASTUFF SEGMENT PUBLIC 'DATA'
In addition you need a stack segment:
STACKSEG SEGMENT STACK 'STACK'
which should have at least 100 bytes for ASMHELP to use.
ASMHELP.OBJ expects that when you call one of its routines the
following conditions are met:
(1) It is a near call, and CS is set to the CODESTUFF
segment.
(2) DS is set to the DATASTUFF segment. Any data which is
longer than one word long and is being transferred must be
in the DATASTUFF segment and its offset address must be in
AX. For one byte or one word data, the data itself is in
AX/AL.
(3) There is available stack space.
In order to link properly, any subroutine which is called must
have an EXTRN statement.
EXTRN show_regs:NEAR
If the above conditions hold, then simply link your object file
with asmhelp.obj:
C> link myfile.obj+asmhelp.obj
and the subroutines will be usable.
ALIASES
The subroutine names were chosen so their functions would be
completely clear. However, they tend to be long so if you use
them with some frequency it would be easier to use aliases. Make
a redefinition macro file and then include it at the beginning of
the program. For instance, if you have:
call print_unsigned_8byte
The PC Assembler Tutor x
______________________
you might want the EQU statement
prt_u8 EQU print_unsigned_8byte
and then rewrite the call:
call prt_u8
Include redefinitions which are reasonable to you and put them in
a file REDEF.MAC.
prt_s8 EQU print_signed_8byte
get_u4 EQU get_unsigned_4byte
show_rw EQU show_regs_and_wait
Then all you need on the first line of your program is:
include REDEF.MAC
and the assembler will do the work for you.
MEMORY RESIDENT SHOW_REGS
If you are not using a debugger, it is possible to have the
show_regs portion of The Assembler Helper resident in memory.
This means that once it is installed, it will stay there till you
turn the machine off or reset the machine. The name of the
program is HELPMEM.COM and it is in \XTRAFILE. It operates
exactly the same way as show_regs except you get to it by using
INT 3. At that point you can set TF to do single stepping.
You load it into memory by typing:
>helpmem
and it will wait for you to send interrupts.
The first time you do INT 3, you will see the normal show_regs
screen. The count is reset to 0 each time you have an INT 3.
There will also be two menu lines:
----------
0=clear TF ; X=set TF ; A=regs from ax ; B=set count ; C=continue
1=ax ; 2=bx ; 3=cx ; 4=dx ; 5=si ; 6=di ; 7=bp ; 8=sp
----------
This is pretty clear. 0 clears TF and X sets TF. If you press B
you will be prompted for a new count number. C lets you continue.
You can set the registers individually. Each register has a
number; ax is 1, bx is 2, etc. When you press the number, you
will be prompted for a HEX style code. This is the same style
code you have been using all the time.
Finally, if you want to transfer the style information from your
program, do the following:
Appendix I - The PC Assembler Helper xi
____________________________________
mov ax, offset ax_byte
int 3
Load the address of ax_byte in AX before the interrupt, and then
press selection A. The 8 bytes located at the address in AX will
be transfered to HELPMEM. The advantage of this is that you can
run ASMHELP concurrently, and they will both show the same
register styles.
When you move into single step mode, you get a different prompt
line:
----------
0 = clear TF and continue ; 2 = menu ; other keys = continue
----------
0 clears TF, 2 sends you to the above menu, and any other key
will continue the program with TF set.
As is true with all debuggers, you should not single step through
subroutine calls since the subroutine might have hundreds or
thousands of steps. In addition, the subroutines in ASMHELP store
the flags. This means that they may store the flags with the trap
flag set. If you clear the trap flag while inside ASMHELP, when
the code exits ASMHELP it will POP the flags with the trap flag
set. If this happens, keep pressing 0 until you get out of single
step mode.
The memory resident version works fine for single stepping as
long as there are no interrupts or subroutine calls. The
show_regs in ASMHELP works fine inbetween subroutine calls but
requires a lot of coding for single stepping. Therefore, a
strategy for using these two programs in conjunction is:
1) use the same register style definition array for both
programs.
2) when there is only 8086 code with no interrupts or
subroutine calls, use INT 3 and single step.
3) When there are interrupts and subroutines interspersed
with the 8086 instructions, switch to ASMHELP.
The screens should look the same except the count will be
different.
I/O
All data i/o is done to the current screen at the current cursor
position. As long as you are in a text mode, ASMHELP should write
to whatever is current.
The PC Assembler Tutor xii
______________________
SHOW_REGS is a different matter. The only allowed modes are 2, 3
and 7. If you enter in modes 2, 3 or 7, it will keep the same
mode. If it is in any other mode, the video card will be forced
to mode 3.
The memory resident version does almost the same thing. Every
time it is called, it checks for modes 2, 3 or 7, and if the mode
is not one of these, changes the mode to mode 3. If the mode is
3, then HELPMEM looks at the first character on page 0 and uses
the attribute of that character for all its display operations.
Whatever display attribute is in the upper left hand corner of
page 0 will be the display attribute for everything.
The register display occupies the first 10 lines of page 0 of
whatever mode you are in. This is written directly to memory and
cannot be changed. Both versions change the page to page 0 upon
entry. If you are on another page, after a call to show_regs you
will be on page 0 at its current cursor position.
If you use ASMHELP in a programming environment or with a
debugger there may be conflicts as to who controls the screen
display. HELPMEM.COM should not be used in a programming
environment and cannot be used with a debugger, since both
HELPMEM and the debugger try to use the same interrupts.